import { ChangeFloorCommand, GetFloorsDataCommand } from "../command/floor.command"
import SdkCommand from "../command/sdk.command"
import floorsEventEnum from "../enum/floorsEvent.enum"
import { AggregateSubscription, SdkCollection, SdkObservable } from "./observable"
import { ClassFactory } from "./utils"
class FloorSubFactory {
  wasInTransition: boolean
  currentFloor: string
  constructor() {
    this.wasInTransition = !1
    this.currentFloor = ""
  }
  create(e, t, o, n) {
    return new AggregateSubscription(
      t.makeFloorChangeSubscription(() => this.throttleCurrentFloorChanges(e, t)),
      n.onChanged(() => this.throttleCameraTranstionChanges(e, n, o)),
      o.onChanged(() => this.throttleCameraTranstionChanges(e, n, o))
    )
  }
  throttleCameraTranstionChanges(e, t, o) {
    const active = t.transition.active || o.transition.active
    active !== this.wasInTransition && ((this.wasInTransition = active), e.onChanged())
  }
  throttleCurrentFloorChanges(e, t) {
    const o = t.currentFloorId
    o !== this.currentFloor && ((this.currentFloor = o), e.onChanged())
  }
}
class CurrentFloorObservableData {
  _data: { id: any; sequence: any; name: string }
  SweepAlignmentType: any
  SweepPlacementType: any
  constructor(e) {
    this._data = { id: void 0, sequence: void 0, name: "" }
    this.SweepAlignmentType = e.SweepAlignmentType
    this.SweepPlacementType = e.SweepPlacementType
  }
  get data() {
    return this._data
  }
  equals(e) {
    const t = this.data,
      o = e.data
    return t.id === o.id && t.sequence === o.sequence && t.name === o.name
  }
  copy(e) {
    this._data.id = e.data.id
    this._data.sequence = e.data.sequence
    this._data.name = e.data.name
  }
  update(e, t, o, n) {
    const cfloor = e.currentFloor
    const sweep = t.currentSweep && t.getSweep(t.currentSweep)
    const sameFloor = () => {
      if (t.transitionActive && t.transition.to && t.transition.from) {
        const e = t.getSweep(t.transition.to),
          o = t.getSweep(t.transition.from)
        return e.floorId !== o.floorId
      }
      return !1
    }
    if ((o.transition.active && !t.transitionActive) || sameFloor()) {
      this._data.id = ""
      this._data.sequence = undefined
      this._data.name = ""
    } else {
      sweep && sweep.alignmentType === this.SweepAlignmentType.UNALIGNED && sweep.placementType === this.SweepPlacementType.UNPLACED
        ? ((this._data.id = undefined), (this._data.sequence = undefined), (this._data.name = ""))
        : cfloor
        ? ((this._data.id = n.getIdFromCwfId(cfloor.id)), (this._data.sequence = cfloor.index), (this._data.name = cfloor.name))
        : ((this._data.id = ""), (this._data.sequence = -1), (this._data.name = "all"))
    }
  }
}
class CollectionDataFloorSubFactory {
  create(e, t) {
    return t.onChanged(() => e.onChanged())
  }
}
class FloorDataCollection {
  _data: {}
  constructor() {
    this._data = {}
  }
  get data() {
    return this._data
  }
  isItemEqual(e, t) {
    const o = this.data[t],
      n = e.data[t]
    return o.id === n.id && o.name === n.name && o.sequence === n.sequence
  }
  update(e, t) {
    for (const o of e.getCollection()) {
      const e = t.getIdFromCwfId(o.id),
        n = this._data[e] || {}
      n.id = e
      n.sequence = o.index
      n.name = o.name
      this._data[e] = n
    }
    for (const e in this.data) {
      t.getFloorForId(e) || delete this._data[e]
    }
  }
  clear() {
    this._data = {}
  }
}
async function floorInfo(e) {
  try {
    const { currentFloor, totalFloors } = e,
      floorNames = e.getFloorNames()
    return { currentFloor: currentFloor ? currentFloor.index : -1, floorNames, totalFloors }
  } catch (e) {
    throw Error("no floors currently loaded")
  }
}

class ShowAllFloorsExecutor {
  issueCommand: any
  ShowAllFloorsCommand: any
  constructor(e, t) {
    this.issueCommand = t.issueCommand
    this.ShowAllFloorsCommand = e.ShowAllFloorsCommand
  }
  validateInput(e) {
    return e
  }
  async exec(e, t, o) {
    try {
      await this.issueCommand(new this.ShowAllFloorsCommand({ moveCamera: !0 }))
      const e = o.currentFloor
      t.return(e ? e.index : -1)
    } catch (e) {
      t.throw("Could not show all floors")
    }
  }
}

export default {
  current: (sdk, exportOBJ, dependencies) => {
    const observable = SdkObservable.create(dependencies, new FloorSubFactory(), new ClassFactory(CurrentFloorObservableData, exportOBJ))
    sdk.addObservableToInterface({ namespace: "Floor", name: "current" }, observable)
  },
  data: (sdk, dependencies) => {
    const data = SdkCollection.create(dependencies, new CollectionDataFloorSubFactory(), new ClassFactory(FloorDataCollection))
    sdk.addCollectionToInterface({ namespace: "Floor", name: "data" }, data)
  },
  getData: (sdk, allFunc, dependencies) => {
    allFunc.addBinding(GetFloorsDataCommand, async () => {
      const [e] = await dependencies.getDependencies()
      return floorInfo(e)
    })
    sdk.addCommandCreator({ namespace: "Floor", name: "getData", args: [] }, () => new GetFloorsDataCommand())
  },
  moveTo: (sdk, exportOBJ, allFunc, dependencies) => {
    dependencies.getDependencies().then(([e]) =>
      allFunc.addBinding(ChangeFloorCommand, async n => {
        if ("number" != typeof n.floorIndex || n.floorIndex < 0) throw Error("floor index must be a non-negative number")
        try {
          const r = "boolean" == typeof n.moveCamera && !n.moveCamera,
            a = r ? 250 : void 0
          await allFunc.issueCommand(new exportOBJ.MoveToFloorIndexCommand(n.floorIndex, r, a))
          const currentFloor = e.currentFloor
          return currentFloor ? currentFloor.index : -1
        } catch (e) {
          throw Error(`Could not move to floor at index ${n.floorIndex}`)
        }
      })
    )
    sdk.addCommandCreator({ namespace: "Floor", name: "moveTo", args: ["floorIndex", "moveCamera"] }, e => new ChangeFloorCommand(e))
  },
  showAll: (sdk, exportOBJ, allFunc, dependencies) => {
    const command = SdkCommand.create(dependencies, new ClassFactory(ShowAllFloorsExecutor, exportOBJ, allFunc))
    sdk.addCommandToInterface({ namespace: "Floor", name: "showAll", args: ["moveCamera"] }, command)
  },
  Event: sdk => {
    sdk.addEnumToInterface({ namespace: "Floor", name: "Event", values: floorsEventEnum })
  },
  onEvent: (sdk, exportOBJ, allFunc, dependencies) => {
    dependencies.getDependencies().then(([n]) => {
      const r = [-1, -1]
      function StartMove(e) {
        if (n) {
          const t = e.to ? n.getFloor(e.to) : null
          const o = e.from ? n.getFloor(e.from) : null
          t && (r[0] = t.index)
          o && (r[1] = o.index)
        }
        return r
      }
      const s = [-1, ""]
      function EndMove(e) {
        if (n) {
          const t = e.floorId ? n.getFloor(e.floorId) : null
          t && (s[0] = t.index)
        }
        s[1] = e.floorName
        return s
      }
      allFunc.subscribe(exportOBJ.StartMoveToFloorMessage, t => sdk.broadcast(floorsEventEnum.CHANGE_START, StartMove, t))
      allFunc.subscribe(exportOBJ.EndMoveToFloorMessage, t => sdk.broadcast(floorsEventEnum.CHANGE_END, EndMove, t))
    })
  }
}
